﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.Dialog;
using Peter.Common.MainMenu;
using Peter.Sdw.Data;
using Peter.Sdw.Properties;

namespace Peter.Sdw.Logic
{
   /// <summary>
   /// Represents a version item.
   /// </summary>
   public class VersionModel : BaseModel
   {
      private int m_TermId;
      private string m_Title;
      private string m_Contents;
      private string m_TermName;
      private string m_PublishDate;
      private string m_SourceName;
      private string m_SourceUrl;
      private AddListModel m_AddContent;
      private RelayCommand m_CmdSetDefault;
      private RelayCommand m_CmdSelectTerm;
      private RelayCommand m_CmdAddSubBooks;
      private SubBookModel m_SelectedSubBook;
      private readonly ObservableCollection<SubBookModel> m_SubBooks;

      /// <summary>
      /// Initializes a new version model.
      /// </summary>
      /// <param name="book">The  parent book.</param>
      public VersionModel (BookModel book)
         : base (null)
      {
         this.Book = book;
         this.m_SubBooks = new ObservableCollection <SubBookModel> ();
      }

      /// <summary>
      /// Gets the parent book.
      /// </summary>
      public BookModel Book { get; private set; }

      #region Version Data

      /// <summary>
      /// Gets or Sets the title.
      /// </summary>
      public string Title
      {
         get { return this.m_Title; }
         set
         {
            if (this.m_Title != value) {
               this.m_Title = value;
               this.RaisePropertyChanged ("Title");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the contents of this version.
      /// </summary>
      public string Contents
      {
         get { return this.m_Contents; }
         set
         {
            if (this.m_Contents != value) {
               this.m_Contents = value;
               this.RaisePropertyChanged ("Contents");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the date the version was published.
      /// </summary>
      public string PublishDate
      {
         get { return this.m_PublishDate; }
         set
         {
            if (this.m_PublishDate != value) {
               this.m_PublishDate = value;
               this.RaisePropertyChanged ("PublishDate");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the chapter id to read when opeing a version.
      /// </summary>
      public int DefaultReadChapter { get; set; }

      /// <summary>
      /// Gets or Sets the last time this was modified.
      /// </summary>
      public DateTime Modified { get; set; }

      /// <summary>
      /// Gets or Sets the associated term.
      /// </summary>
      public int TermId
      {
         get { return this.m_TermId; }
         set
         {
            if (this.m_TermId != value) {
               this.m_TermId = value;
               this.RaisePropertyChanged ("TermId");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the name of this source.
      /// </summary>
      public string SourceName
      {
         get { return this.m_SourceName; }
         set
         {
            if (this.m_SourceName != value) {
               this.m_SourceName = value;
               this.RaisePropertyChanged ("SourceName");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the url for the source.
      /// </summary>
      public string SourceUrl
      {
         get { return this.m_SourceUrl; }
         set
         {
            if (this.m_SourceUrl != value) {
               this.m_SourceUrl = value;
               this.RaisePropertyChanged ("SourceUrl");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the term name.
      /// </summary>
      public string TermName
      {
         get { return this.m_TermName; }
         set
         {
            if (this.m_TermName != value) {
               this.m_TermName = value;
               this.RaisePropertyChanged ("TermName");
            }
         }
      }

      #endregion

      /// <summary>
      /// Gets the list of sub books.
      /// </summary>
      public ObservableCollection<SubBookModel> SubBooks { get { return this.m_SubBooks; } }

      /// <summary>
      /// Gets or Sets the selected sub book.
      /// </summary>
      public SubBookModel SelectedSubBook
      {
         get { return this.m_SelectedSubBook; }
         set
         {
            this.m_SelectedSubBook = value;
            if (this.m_SelectedSubBook != null)
               this.m_SelectedSubBook.GetChapters ();
            this.RaisePropertyChanged ("SelectedSubBook");
         }
      }

      /// <summary>
      /// Gets the command used to set the default version.
      /// </summary>
      public ICommand SetAsDefaultCommand
      {
         get { return this.m_CmdSetDefault ?? (this.m_CmdSetDefault = new RelayCommand (this.OnSetDefault, this.CanSetDefault)); }
      }

      /// <summary>
      /// Gets the select term command.
      /// </summary>
      public ICommand SelectTermCommand
      {
         get { return this.m_CmdSelectTerm ?? (this.m_CmdSelectTerm = new RelayCommand (this.OnSelectTerm)); }
      }

      /// <summary>
      /// Gets the command used to add sub books.
      /// </summary>
      public ICommand AddSubBooksCommand
      {
         get { return this.m_CmdAddSubBooks ?? (this.m_CmdAddSubBooks = new RelayCommand (this.OnAddSubBooks, this.CanAddSubBooks)); }
      }

      /// <summary>
      /// Checks to see if the command can execute.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if the command can execute, otherwise false.</returns>
      private bool CanAddSubBooks (object obj)
      {
         return this.Id > 0 ;
      }

      /// <summary>
      /// Adds the current list of sub books.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnAddSubBooks (object obj)
      {
         var editor = this.Book.Peter.NewFile ();
         editor.Title = "Add SubBooks";
         editor.Icon = MainMenuIcon.Bookthree;

         if (this.m_AddContent == null) {
            this.m_AddContent = new AddListModel {
               Message = {Message = "Create the list of Sub Books to add. Place each sub book on a new line."}
            };
         }
         this.m_AddContent.Editor = editor;
         this.m_AddContent.Add.AddRequested += this.OnAddContent;
         editor.TopContent = this.m_AddContent.Message;
         editor.RightContent = this.m_AddContent.Range;
         editor.BottomContent = this.m_AddContent.Add;
      }

      /// <summary>
      /// Occurs when the user adds the given content.
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void OnAddContent (object sender, EventArgs e)
      {
         this.m_AddContent.Add.AddRequested -= this.OnAddContent;
         var list = this.m_AddContent.Editor.Text;
         var parameters = new NameValueCollection {
               {Constants.REQUEST_TOKEN, WebQueue.Instance.RequestToken},
               {"id", this.Id.ToString (CultureInfo.InvariantCulture)},
               {"list", list},
            };
         WebQueue.Instance.WebPostComplete += this.OnPostCompleted;
         WebQueue.Instance.Post (Constants.URL_CREATE_SUB_BOOKS, parameters);
         this.Book.Peter.CloseDocument (this.m_AddContent.Editor);
         this.m_AddContent.Editor = null;
      }

      /// <summary>
      /// Shows the select term dialog.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnSelectTerm (object obj)
      {
         if (this.Book.Peter.ShowDialog (SdwMainModel.TermSelector) != DialogCloseAction.Ok) return;
         var type = obj.ToString ();
         if (type == "version") {
            this.TermName = SdwMainModel.TermSelector.TermList.SelectedTerm.Name;
            this.TermId = SdwMainModel.TermSelector.TermList.SelectedTerm.Id;
         } else {
            this.SelectedSubBook.TermName = SdwMainModel.TermSelector.TermList.SelectedTerm.Name;
            this.SelectedSubBook.TermId = SdwMainModel.TermSelector.TermList.SelectedTerm.Id;
         }
      }

      /// <summary>
      /// Occurs when a property in the version changes.
      /// </summary>
      private void Edit ()
      {
         if (string.IsNullOrEmpty (this.Title) || this.TermId == 0 || !OkToUpdate) return;
         var parameters = new NameValueCollection
         {
            {Constants.REQUEST_TOKEN, WebQueue.Instance.RequestToken},
            {"id", this.Id.ToString (CultureInfo.InvariantCulture)},
            {"bookId", this.Book.Id.ToString (CultureInfo.InvariantCulture)},
            {"title", this.Title},
            {"date", this.PublishDate},
            {"sourceUrl", this.SourceUrl ?? string.Empty},
            {"sourceName", this.SourceName ?? string.Empty},
            {"termId", this.TermId.ToString (CultureInfo.InvariantCulture)}
         };
         WebQueue.Instance.WebPostComplete += this.OnPostCompleted;
         WebQueue.Instance.Post (this.Id == 0 ? Constants.URL_CREATE_VERSION : Constants.URL_EDIT_VERSION, parameters);
      }

      /// <summary>
      /// Checks to see if the set default version command can execute.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if ok to execute, otherwise false.</returns>
      private bool CanSetDefault (object obj)
      {
         return this.Id > 0;
      }

      /// <summary>
      /// Sets the default version.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnSetDefault (object obj)
      {
         var parameters = new NameValueCollection
            {
               {Constants.REQUEST_TOKEN, WebQueue.Instance.RequestToken},
               {"bookId", this.Book.Id.ToString (CultureInfo.InvariantCulture)},
               {"versionId", this.Id.ToString (CultureInfo.InvariantCulture)},
            };
         WebQueue.Instance.WebPostComplete += this.OnPostCompleted;
         WebQueue.Instance.Post (Constants.URL_DEFULT_VERSION, parameters);
      }

      /// <summary>
      /// Gets the list of version from the server.
      /// </summary>
      public void GetSubBooks ()
      {
         this.IsLoading = true;
         this.SubBooks.Clear ();
         WebQueue.Instance.WebGetComplete += this.OnGetCompleted;
         WebQueue.Instance.Get (string.Format (Constants.URL_SUB_BOOK_LIST, this.Id));
      }

      /// <summary>
      /// Adds the given sub books to the current list.
      /// </summary>
      /// <param name="response"></param>
      private void AddSubBooks (dynamic response)
      {
         foreach (var subBook in response.subbooks) {
            this.SubBooks.Add (new SubBookModel (this) {
               Id = subBook.id,
               Hide = subBook.hide,
               Alias = subBook.alias,
               Order = subBook.order,
               TermId = subBook.termid,
               TermName = subBook.termname
            });
         }
         foreach (var subBook in this.SubBooks) {
            subBook.OkToUpdate = true;
         }
      }

      /// <summary>
      /// Checks to see if this item can be deleted or not.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      protected override bool CanDelete (object obj)
      {
         return this.SelectedSubBook != null;
      }

      /// <summary>
      /// Occurs when a web request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnGetCompleted (object sender, WebRequestEventArgs e)
      {
         try {
            if (e.Url == string.Format (Constants.URL_SUB_BOOK_LIST, this.Id)) {
               WebQueue.Instance.WebGetComplete -= this.OnGetCompleted;
               if (e.Response.status == Constants.SUCCESS) {
                  this.AddSubBooks (e.Response);
               }
               this.IsLoading = false;
            }
         } catch (Exception ex) {
            Console.Error.WriteLine (ex.Message);
         }
      }

      /// <summary>
      /// Occurs when a web post request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnPostCompleted (object sender, WebRequestEventArgs e)
      {
         if (e.Url == Constants.URL_CREATE_VERSION) {
            WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
            if (e.Response.status == Constants.SUCCESS) {
               Console.WriteLine (Resources.Version_Created);
               this.Id = e.Response.id;
            }
         } else if (e.Url == Constants.URL_EDIT_VERSION) {
            WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
            if (e.Response.status == Constants.SUCCESS)
               Console.WriteLine (Resources.Edit_successful);
         } else if (e.Url == Constants.URL_DEFULT_VERSION) {
            WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
            if (e.Response.status == Constants.SUCCESS) {
               Console.WriteLine (Resources.Default_version_set);
               this.Book.DefaultVersionId = this.Id;
            }
         } else if (e.Url == Constants.URL_CREATE_SUB_BOOKS) {
            WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
            if (e.Response.status == Constants.SUCCESS) {
               Console.WriteLine (Resources.Subbooks_Created);
               this.AddSubBooks (e.Response);
            }
         }
      }
   }
}
